Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Displaying Raw • Download


meshchatx/src/path_utils.py dff83e198e78b84ec225e53ce3ab119f598b0a01 (dff83e19) Text, 4.79 KB

T8b949e# SPDX-License-Identifier: 0BSD

T8b949e"""Filesystem and HTTP client helpers used at startup and in the web layer."""

Tff7b72import T7ee787os
Tff7b72import T7ee787sys
Tff7b72import T7ee787tempfile

Tff7b72from T7ee787aiohttp Tff7b72import Te6edf3web


Tff7b72def Td2a8ffis_path_within_dirTb4b4b4(Te6edf3pathTb4b4b4: Tffa657strTb4b4b4, Te6edf3directoryTb4b4b4: Tffa657strTb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
T8b949e"""Return True when path resolves inside directory (realpath + separator)."""
Tff7b72if Tff7b72not Te6edf3path Tff7b72or Tff7b72not Te6edf3directoryTb4b4b4:
Tff7b72return Tff7b72False
Te6edf3candidate Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffnormcaseTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffnormpathTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffrealpathTb4b4b4(Te6edf3pathTb4b4b4)Tb4b4b4)Tb4b4b4)
Te6edf3root Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffnormcaseTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffnormpathTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffrealpathTb4b4b4(Te6edf3directoryTb4b4b4)Tb4b4b4)Tb4b4b4)
Tff7b72return Te6edf3candidate Tff7b72== Te6edf3root Tff7b72or Te6edf3candidateTff7b72.Td2a8ffstartswithTb4b4b4(Te6edf3root Tff7b72+ Te6edf3osTff7b72.Td2a8ffsepTb4b4b4)


Tff7b72def Td2a8ffsafe_path_under_dirTb4b4b4(Te6edf3directoryTb4b4b4: Tffa657strTb4b4b4, Te6edf3filenameTb4b4b4: Tffa657strTb4b4b4) Tff7b72-Tff7b72> Tffa657str Tff7b72| Tff7b72NoneTb4b4b4:
T8b949e"""Resolve filename as a basename under directory, or None if unsafe.

Rejects NUL, empty names, dot, dot-dot, and drive-letter basenames.
The result is realpath-checked so it cannot escape directory.
"""
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3directoryTb4b4b4, Tffa657strTb4b4b4) Tff7b72or Tff7b72not Te6edf3directoryTb4b4b4:
Tff7b72return Tff7b72None
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3filenameTb4b4b4, Tffa657strTb4b4b4) Tff7b72or Tff7b72not Te6edf3filename Tff7b72or Ta5d6ff"Tffea00\x00Ta5d6ff" Tff7b72in Te6edf3filenameTb4b4b4:
Tff7b72return Tff7b72None
Te6edf3base Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffbasenameTb4b4b4(Te6edf3filenameTff7b72.Td2a8ffreplaceTb4b4b4(Ta5d6ff"Tffea00\\Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff/Ta5d6ff"Tb4b4b4)Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3base Tff7b72or Te6edf3base Tff7b72in Tb4b4b4{Ta5d6ff"Ta5d6ff.Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff..Ta5d6ff"Tb4b4b4} Tff7b72or Ta5d6ff"Ta5d6ff:Ta5d6ff" Tff7b72in Te6edf3baseTb4b4b4:
Tff7b72return Tff7b72None
Te6edf3path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffrealpathTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3directoryTb4b4b4, Te6edf3baseTb4b4b4)Tb4b4b4)
Te6edf3root Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffrealpathTb4b4b4(Te6edf3directoryTb4b4b4)
Tff7b72if Te6edf3path Tff7b72!= Te6edf3root Tff7b72and Tff7b72not Te6edf3pathTff7b72.Td2a8ffstartswithTb4b4b4(Te6edf3root Tff7b72+ Te6edf3osTff7b72.Td2a8ffsepTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72None
Tff7b72return Te6edf3path


Tff7b72def Td2a8ffresolve_path_under_dirTb4b4b4(Te6edf3directoryTb4b4b4: Tffa657strTb4b4b4, Te6edf3user_pathTb4b4b4: Tffa657strTb4b4b4) Tff7b72-Tff7b72> Tffa657str Tff7b72| Tff7b72NoneTb4b4b4:
T8b949e"""Join user_path under directory and return realpath if contained, else None.

Unlike safe_path_under_dir, relative subpaths are allowed when they stay
inside directory after realpath normalization.
"""
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3directoryTb4b4b4, Tffa657strTb4b4b4) Tff7b72or Tff7b72not Te6edf3directoryTb4b4b4:
Tff7b72return Tff7b72None
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3user_pathTb4b4b4, Tffa657strTb4b4b4) Tff7b72or Tff7b72not Te6edf3user_path Tff7b72or Ta5d6ff"Tffea00\x00Ta5d6ff" Tff7b72in Te6edf3user_pathTb4b4b4:
Tff7b72return Tff7b72None
Te6edf3cleaned Tff7b72= Te6edf3user_pathTff7b72.Td2a8ffreplaceTb4b4b4(Ta5d6ff"Tffea00\\Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff/Ta5d6ff"Tb4b4b4)Tff7b72.Td2a8fflstripTb4b4b4(Ta5d6ff"Ta5d6ff/Ta5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3cleaned Tff7b72or Te6edf3cleaned Tff7b72in Tb4b4b4{Ta5d6ff"Ta5d6ff.Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff..Ta5d6ff"Tb4b4b4}Tb4b4b4:
Tff7b72return Tff7b72None
Te6edf3joined Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3directoryTb4b4b4, Te6edf3cleanedTb4b4b4)
Tff7b72if Tff7b72not Te6edf3is_path_within_dirTb4b4b4(Te6edf3joinedTb4b4b4, Te6edf3directoryTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72None
Tff7b72return Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffrealpathTb4b4b4(Te6edf3joinedTb4b4b4)


Tff7b72def Td2a8ffresolve_log_dirTb4b4b4(Tb4b4b4)Tb4b4b4:
T8b949e"""Choose a writable log directory across container, desktop, and Windows."""
Te6edf3env_dir Tff7b72= Te6edf3osTff7b72.Td2a8ffenvironTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffMESHCHAT_LOG_DIRTa5d6ff"Tb4b4b4)
Te6edf3candidates Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72if Te6edf3env_dirTb4b4b4:
Te6edf3candidatesTff7b72.Td2a8ffappendTb4b4b4(Te6edf3env_dirTb4b4b4)

Te6edf3storage_dir Tff7b72= Te6edf3osTff7b72.Td2a8ffenvironTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffMESHCHAT_STORAGE_DIRTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3storage_dirTb4b4b4:
Te6edf3candidatesTff7b72.Td2a8ffappendTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3storage_dirTb4b4b4, Ta5d6ff"Ta5d6fflogsTa5d6ff"Tb4b4b4)Tb4b4b4)

Te6edf3candidatesTff7b72.Td2a8ffappendTb4b4b4(Ta5d6ff"Ta5d6ff/config/logsTa5d6ff"Tb4b4b4)

Tff7b72if Te6edf3osTff7b72.Td2a8ffname Tff7b72== Ta5d6ff"Ta5d6ffntTa5d6ff"Tb4b4b4:
Te6edf3appdata Tff7b72= Te6edf3osTff7b72.Td2a8ffenvironTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffLOCALAPPDATATa5d6ff"Tb4b4b4) Tff7b72or Te6edf3osTff7b72.Td2a8ffenvironTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffAPPDATATa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3appdataTb4b4b4:
Te6edf3candidatesTff7b72.Td2a8ffappendTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3appdataTb4b4b4, Ta5d6ff"Ta5d6ffMeshChatXTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6fflogsTa5d6ff"Tb4b4b4)Tb4b4b4)

Te6edf3home_dir Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexpanduserTb4b4b4(Ta5d6ff"Ta5d6ff~Ta5d6ff"Tb4b4b4)
Te6edf3candidatesTff7b72.Td2a8ffappendTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3home_dirTb4b4b4, Ta5d6ff"Ta5d6ff.reticulum-meshchatxTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6fflogsTa5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3candidatesTff7b72.Td2a8ffappendTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3tempfileTff7b72.Td2a8ffgettempdirTb4b4b4(Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffmeshchatxTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6fflogsTa5d6ff"Tb4b4b4)Tb4b4b4)

Tff7b72for Te6edf3path Tff7b72in Te6edf3candidatesTb4b4b4:
Tff7b72if Tff7b72not Te6edf3pathTb4b4b4:
Tff7b72continue
Tff7b72tryTb4b4b4:
Te6edf3osTff7b72.Td2a8ffmakedirsTb4b4b4(Te6edf3pathTb4b4b4, Te6edf3exist_okTff7b72=Tff7b72TrueTb4b4b4)
Tff7b72return Te6edf3path
Tff7b72except Tf85149PermissionErrorTb4b4b4:
Tff7b72continue
Tff7b72except Tf85149OSErrorTb4b4b4:
Tff7b72continue

Tff7b72return Tff7b72None


Tff7b72def Td2a8ffrequest_client_ipTb4b4b4(
Te6edf3requestTb4b4b4: Te6edf3webTff7b72.Td2a8ffRequestTb4b4b4,
Te6edf3trusted_proxy_cidrsTb4b4b4: Tffa657str Tff7b72| Tff7b72None Tff7b72= Tff7b72NoneTb4b4b4,
Tb4b4b4) Tff7b72-Tff7b72> Tffa657strTb4b4b4:
T8b949e"""Return the client IP, trusting X-Forwarded-For only from configured proxies.

When trusted_proxy_cidrs is empty, X-Forwarded-For is ignored so clients
cannot spoof allowlist or login lockout keys.
"""
Te6edf3remote Tff7b72= Tb4b4b4(Te6edf3requestTff7b72.Td2a8ffremote Tff7b72or Ta5d6ff"Ta5d6ff"Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
Te6edf3xff Tff7b72= Te6edf3requestTff7b72.Td2a8ffheadersTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffX-Forwarded-ForTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3xff Tff7b72and Te6edf3trusted_proxy_cidrsTb4b4b4:
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787ip_allowlist Tff7b72import Te6edf3client_ip_allowed

Tff7b72if Te6edf3remote Tff7b72and Te6edf3client_ip_allowedTb4b4b4(Te6edf3remoteTb4b4b4, Te6edf3trusted_proxy_cidrsTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3xffTff7b72.Td2a8ffsplitTb4b4b4(Ta5d6ff"Ta5d6ff,Ta5d6ff"Tb4b4b4)Tb4b4b4[T79c0ff0Tb4b4b4]Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
Tff7b72return Te6edf3remote


Tff7b72def Td2a8ffget_file_pathTb4b4b4(Te6edf3filenameTb4b4b4)Tb4b4b4:
T8b949e# NOTE: this is required to be able to pack our app with cxfreeze as an exe, otherwise it can't access bundled assets
T8b949e# this returns a file path based on if we are running meshchat.py directly, or if we have packed it as an exe with cxfreeze
T8b949e# https://cx-freeze.readthedocs.io/en/latest/faq.html#using-data-files
T8b949e# bearer:disable python_lang_path_traversal
Te6edf3filename Tff7b72= Te6edf3filenameTff7b72.Td2a8ffrstripTb4b4b4(Ta5d6ff"Ta5d6ff/Tffea00\\Ta5d6ff"Tb4b4b4)

Tff7b72if Tffa657getattrTb4b4b4(Te6edf3sysTb4b4b4, Ta5d6ff"Ta5d6fffrozenTa5d6ff"Tb4b4b4, Tff7b72FalseTb4b4b4)Tb4b4b4:
Te6edf3datadir Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffdirnameTb4b4b4(Te6edf3sysTff7b72.Td2a8ffexecutableTb4b4b4)
Tff7b72return Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3datadirTb4b4b4, Te6edf3filenameTb4b4b4)

Te6edf3package_dir Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffdirnameTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffdirnameTb4b4b4(Tff7b72__file__Tb4b4b4)Tb4b4b4)
Te6edf3package_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3package_dirTb4b4b4, Te6edf3filenameTb4b4b4)
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3package_pathTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3package_path

Te6edf3repo_root Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffdirnameTb4b4b4(Te6edf3package_dirTb4b4b4)
Te6edf3repo_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3repo_rootTb4b4b4, Te6edf3filenameTb4b4b4)
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3repo_pathTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3repo_path

Tff7b72return Te6edf3package_path


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────